local addon = LibStub("AceAddon-3.0"):NewAddon("Poppins", "AceConsole-3.0", "AceEvent-3.0")

local LibButtonFacade
local LibStickyFrames = LibStub("LibStickyFrames-2.0")
local LibKeyBound = LibStub("LibKeyBound-1.0")
local LibSharedMedia = LibStub("LibSharedMedia-3.0")

local defaultDB = {
	profile = {
		enabled = true,
		lock = false,
		tooltips = 3,
		buttons = {
			count = 4,
			["**"] = {
				keybinding = false,
				state = "1",
				group = false,
				position = {
					false, -- x
					false, -- y
					false, -- relId
					false, -- pointA
					false, -- pointB
				},
				popups = {
					count = 4,
					["**"] = {
						["**"] = {
							type = false,
							item = false,
							spell = false,
							macro = false,
						},
					},
				},
			},
		},
		groups = {
			["**"] = {
				-- display
				scale = 1,
				popupscale = 1,
				strata = "MEDIUM",
				popupstrata = "HIGH",
				direction = "TOP",
				offset = false,
				padding = 0,
				alpha = 1,
				popupalpha = 1,
				combatAlpha = false,
				hoverAlpha = false,
				-- text
				hotkey = {
					show = true,
					font = "Friz Quadrata TT",
					size = 10,
					justify = "RIGHT",
					x = 17,
					y = 11,
				},
				count = {
					show = true,
					font = "Friz Quadrata TT",
					size = 10,
					justify = "RIGHT",
					x = 17,
					y = -10,
				},
				selected = {
					show = true,
					font = "Friz Quadrata TT",
					size = 10,
					justify = "LEFT",
					x = -18,
					y = 11,
				},
				-- actions
				clicks = {
					1, -- left
					2, -- right
					3, -- middle
				},
				-- skin
				skin = {
					false, -- skin
					false, -- gloss
					false, -- backdrop
					{}, -- colors
				},
			},
		},
	},
}

local function loadOptions()
	local name = format("%sOptions", addon.name)
	local loaded, reason = LoadAddOn(name)
	if not loaded then
		addon:Print(format("%s: %s", name, _G[format("ADDON_%s", reason or "UNKNOWN_ERROR")]))
	end
end

function addon:OnInitialize()
	self.db = LibStub("AceDB-3.0"):New(self.name.."DB", defaultDB, "char")
	self.db.RegisterCallback(self, "OnProfileChanged")
	self.db.RegisterCallback(self, "OnProfileReset", "OnProfileChanged")
	self:SetEnabledState(self.db.profile.enabled)
	self:RegisterChatCommand(addon.name, loadOptions)

	LibButtonFacade = LibButtonFacade or LibStub("LibButtonFacade", true)
	if LibButtonFacade then
		LibButtonFacade:RegisterSkinCallback(self.name, self.LibButtonFacade_Update, self)
	end
end

function addon:OnEnable()
	self.db.profile.enabled = true
	if not self.header then
		self.header = CreateFrame("Frame", self.name.."Header", UIParent, "SecureStateHeaderTemplate")
		self.header:SetPoint("CENTER", UIParent)
		self.header:Show()
		self.buttons = {}
		self.groups = {}
	end

	LibKeyBound.RegisterCallback(self, "LIBKEYBOUND_ENABLED", "UpdateColors")
	LibKeyBound.RegisterCallback(self, "LIBKEYBOUND_DISABLED", "UpdateColors")
	LibKeyBound.RegisterCallback(self, "LIBKEYBOUND_MODE_COLOR_CHANGED", "UpdateColors")

	for group in pairs(self.db.profile.groups) do
		if type(group) == "string" then
			self:AddGroup(group)
		end
	end

	addon:UpdateAllButtons()
end

function addon:OnDisable()
	self.db.profile.enabled = false
	LibKeyBound.UnregisterAllCallbacks(self)
	for id in ipairs(self.buttons) do
		self:RemoveButton(id)
	end
end

function addon:OnProfileChanged()
	local enabled = self.db.profile.enabled
	if enabled ~= self:IsEnabled() then
		if enabled then
			self:Enable()
		else
			self:Disable()
		end
	else
		self:OnDisable()
		if enabled then
			self:OnEnable()
		end
	end
end

function addon:UpdateAllButtons()
	local count = self.db.profile.buttons.count
	for id = count + 1, #self.buttons do
		self:RemoveButton(id)
	end
	for id = 1, count do
		self:UpdateButton(id)
	end
	self:UpdateColors()
end

function addon:NewButton()
	local db = self.db.profile.buttons
	db.count = db.count + 1
	self:UpdateButton(db.count)
end

function addon:DeleteButton(id)
	local buttons = self.db.profile.buttons
	local groups = self.db.profile.groups
	for i=1, (buttons.count - 1) do
		if i >= id then
			buttons[i] = buttons[i + 1]
			groups[i] = groups[i + 1]
			i = i + 1
		end
		local relId = buttons[i].position[3]
		if relId == id then
			self.buttons[i]:Anchor()
		elseif relId and relId > id then
			buttons[i].position[3] = frame - 1
		end
	end
	buttons[buttons.count] = nil
	groups[buttons.count] = nil
	buttons.count = buttons.count - 1
	self:UpdateAllButtons()
end

function addon:UpdateButton(id)
	local settings = self.db.profile.buttons[id]
	local button = self.buttons[id]
	if not button then
		button = self.Frame:Retrieve(id)
		self.buttons[id] = button
	end

	local group = settings.group
	if group and not self:GetGroup(group) then
		self:AddGroup(group)
	end

	button:SetSettings(settings)
	button:SetOptions(addon.db.profile.groups[group or id])

	return button
end

function addon:RemoveButton(id)
	self.buttons[id] = self.buttons[id]:Store()
end

function addon:GetButton(id)
	return self.buttons[id]
end

function addon:NewGroup(group)
	self.db.profile.groups[group].create = true
	self:AddGroup(group)
end

function addon:DeleteGroup(group)
	for id, frame in self:GetGroup(group):IterateFrames() do
		frame:SetGroup(nil)
	end
	self.db.profile.groups[group] = nil
	self:RemoveGroup(group)
end

function addon:AddGroup(group)
	local info = self:GetGroup(group) or self.Group:Create(group)
	self.groups[group] = info
	return info
end

function addon:RemoveGroup(group)
	self.groups[group] = self.groups[group]:Destroy()
end

function addon:GetGroup(group)
	return self.groups[group]
end

local copy
function copy(src, dest)
	if type(dest) ~= "table" then dest = {} end
	if type(src) == "table" then
		for k,v in pairs(src) do
			if type(v) == "table" then
				v = copy(v, dest[k])
			end
			dest[k] = v
		end
	end
	return dest
end
function addon:CopyOptions(src, dest)
	local db = self.db.profile.groups
	return copy(db[src], db[dest])
end

function addon:IsLocked()
	return LibStickyFrames:GetGroup() == true
end

function addon:SetLock(locked)
	addon.header:SetAttribute("state", locked and "-1" or "0")
	LibStickyFrames:SetGroup(locked)
	for id, button in ipairs(self.buttons) do
		button:SetLock(locked)
		button.button:IsMouseEnabled()
	end
end

function addon:LibButtonFacade_Update(skin, gloss, backdrop, group, button, colors)
	if not group or group == "nil" then return end
	self.db.profile.groups[tonumber(group) or group].skin = { skin, gloss, backdrop, colors }
end

-- LibKeyBound related functions
function addon:UpdateColors()
	for button in self.Frame:Iterate() do
		button:UpdateColor()	
	end
end